using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using Microsoft.SharePoint;

namespace AdvancedLookup
{
    public class AdvancedLookupField : SPFieldLookup
    {
        private string _additionalLookupFields;

        private string _camlQueryFields;

        private string _dialogWidth;

        private string _dialogHeight;

        private string _queryStringParam;

        private string _camlQuery;

        private string _initValueQueryStringParam;

        private string _dialogPageUrl;

        private static readonly Dictionary<int, uint> ItemLimitProp = new Dictionary<int, uint>();

        private static readonly Dictionary<int, uint> PrefixProp = new Dictionary<int, uint>();

        private uint _itemLimit;

        private uint _prefix;

        public AdvancedLookupField(SPFieldCollection fields, string fieldName)
            : base(fields, fieldName)
        {
            Init();
        }

        public AdvancedLookupField(SPFieldCollection fields, string typeName, string displayName)
            : base(fields, typeName, displayName)
        {
            Init();
        }

        public uint Prefix
        {
            get
            {
                return PrefixProp.ContainsKey(ContextId) ? PrefixProp[ContextId] : _prefix;
            }
            set
            {
                _prefix = value;
            }
        }

        public uint ItemLimit
        {
            get
            {
                return ItemLimitProp.ContainsKey(ContextId) ? ItemLimitProp[ContextId] : _itemLimit;
            }
            set
            {
                _itemLimit = value;
            }
        }

        public override void Update()
        {
            UpdateFieldProperties();

            SetCustomProperty("Prefix", Prefix);

            SetCustomProperty("ItemLimit", ItemLimit);

            base.Update();

            if (ItemLimitProp.ContainsKey(ContextId))
                ItemLimitProp.Remove(ContextId);

            if (PrefixProp.ContainsKey(ContextId))
                PrefixProp.Remove(ContextId);

            CleanUpThreadData();
        }

        private void UpdateFieldProperties()
        {
            SetCustomProperty("AdditionalLookupFields", GetFieldThreadDataValue("AdditionalLookupFields", true));

            SetCustomProperty("CamlQueryFields", GetFieldThreadDataValue("CamlQueryFields", true));

            SetCustomProperty("CamlQuery", GetFieldThreadDataValue("CamlQuery", true));

            SetCustomProperty("DialogPageUrl", GetFieldThreadDataValue("DialogPageUrl", true));

            SetCustomProperty("DialogWidth", GetFieldThreadDataValue("DialogWidth", true));

            SetCustomProperty("DialogHeight", GetFieldThreadDataValue("DialogHeight", true));

            SetCustomProperty("QueryStringParam", GetFieldThreadDataValue("QueryStringParam", true));

            SetCustomProperty("InitValueQueryStringParam", GetFieldThreadDataValue("InitValueQueryStringParam", true));
        }

        private void Init()
        {
            if (GetCustomProperty("ItemLimit") != null)
            {
                ItemLimit = (uint)GetCustomProperty("ItemLimit");
            }
            else
            {
                ItemLimit = 10;
            }

            if (GetCustomProperty("Prefix") != null)
            {
                Prefix = (uint)GetCustomProperty("Prefix");
            }
            else
            {
                Prefix = 1;
            }
        }

        public override void OnAdded(SPAddFieldOptions op)
        {
            base.OnAdded(op);

            Update();
        }

        public void UpdatePrefixProp(uint value)
        {
            PrefixProp[ContextId] = value;
        }

        public void UpdateItemLimitProp(uint value)
        {
            ItemLimitProp[ContextId] = value;
        }

        public int ContextId
        {
            get
            {
                if (SPContext.Current != null)
                    return SPContext.Current.GetHashCode();

                return GetHashCode();
            }
        }

        /// <summary>
        /// This method ensures that a value is provided if the field is mandatory
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public override string GetValidatedString(object value)
        {
            if (Required)
            {
                string message = string.Format(CultureInfo.InvariantCulture, "{0} is required.", Title);

                if (value == null)
                {
                    throw new SPFieldValidationException(message);
                }

                SPFieldLookupValue spFieldLookupValue = value as SPFieldLookupValue;

                if (spFieldLookupValue != null && spFieldLookupValue.LookupId < 1 && (string.IsNullOrEmpty(spFieldLookupValue.LookupValue) || spFieldLookupValue.LookupValue == "(None)"))
                {
                    throw new SPFieldValidationException(message);
                }
            }

            return base.GetValidatedString(value);
        }


        private string GetFieldThreadDataValue(string propertyName, bool ignoreEmptyValue)
        {
            string namedDataSlot = (string)Thread.GetData(Thread.GetNamedDataSlot(propertyName));

            if (string.IsNullOrEmpty(namedDataSlot) && !ignoreEmptyValue)
            {
                namedDataSlot = (string)GetCustomProperty(propertyName);
            }

            return namedDataSlot;
        }

        private static void SetFieldThreadDataValue(string propertyName, string value)
        {
            Thread.SetData(Thread.GetNamedDataSlot(propertyName), value);
        }

        private static void CleanUpThreadData()
        {
            Thread.FreeNamedDataSlot("AdditionalLookupFields");

            Thread.FreeNamedDataSlot("CamlQueryFields");

            Thread.FreeNamedDataSlot("CamlQuery");

            Thread.FreeNamedDataSlot("DialogPageUrl");

            Thread.FreeNamedDataSlot("DialogWidth");

            Thread.FreeNamedDataSlot("DialogHeight");

            Thread.FreeNamedDataSlot("QueryStringParam");

            Thread.FreeNamedDataSlot("InitValueQueryStringParam");
        }

        public string AdditionalLookupFields
        {
            get
            {
                if (_additionalLookupFields == null)
                {
                    _additionalLookupFields = GetFieldThreadDataValue("AdditionalLookupFields", false);
                }

                return (!string.IsNullOrEmpty(_additionalLookupFields)) ? _additionalLookupFields : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("AdditionalLookupFields", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string CamlQueryFields
        {
            get
            {
                if (_camlQueryFields == null)
                {
                    _camlQueryFields = GetFieldThreadDataValue("CamlQueryFields", false);
                }

                return (!string.IsNullOrEmpty(_camlQueryFields)) ? _camlQueryFields : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("CamlQueryFields", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string InitValueQueryStringParam
        {
            get
            {
                if (_initValueQueryStringParam == null)
                {
                    _initValueQueryStringParam = GetFieldThreadDataValue("InitValueQueryStringParam", false);
                }
                return (!string.IsNullOrEmpty(_initValueQueryStringParam)) ? _initValueQueryStringParam : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("InitValueQueryStringParam", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string QueryStringParam
        {
            get
            {
                if (_queryStringParam == null)
                {
                    _queryStringParam = GetFieldThreadDataValue("QueryStringParam", false);
                }
                return (!string.IsNullOrEmpty(_queryStringParam)) ? _queryStringParam : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("QueryStringParam", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string CamlQuery
        {
            get
            {
                if (_camlQuery == null)
                {
                    _camlQuery = GetFieldThreadDataValue("CamlQuery", false);
                }
                return (!string.IsNullOrEmpty(_camlQuery)) ? _camlQuery : "<Query><OrderBy><FieldRef Name='Title'/></OrderBy><Where><BeginsWith><FieldRef Name='Title'/><Value Type='Text'>\"+{0}+\"</Value></BeginsWith></Where></Query>";
            }
            set
            {
                SetFieldThreadDataValue("CamlQuery", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string DialogHeight
        {
            get
            {
                if (_dialogHeight == null)
                {
                    _dialogHeight = GetFieldThreadDataValue("DialogHeight", false);
                }
                return (!string.IsNullOrEmpty(_dialogHeight)) ? _dialogHeight : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("DialogHeight", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string DialogWidth
        {
            get
            {
                if (_dialogWidth == null)
                {
                    _dialogWidth = GetFieldThreadDataValue("DialogWidth", false);
                }
                return (!string.IsNullOrEmpty(_dialogWidth)) ? _dialogWidth : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("DialogWidth", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public string DialogPageUrl
        {
            get
            {
                if (_dialogPageUrl == null)
                {
                    _dialogPageUrl = GetFieldThreadDataValue("DialogPageUrl", false);
                }
                return (!string.IsNullOrEmpty(_dialogPageUrl)) ? _dialogPageUrl : string.Empty;
            }
            set
            {
                SetFieldThreadDataValue("DialogPageUrl", (!string.IsNullOrEmpty(value) ? value : ""));
            }
        }

        public override Microsoft.SharePoint.WebControls.BaseFieldControl FieldRenderingControl
        {
            get
            {
                return new AdvancedLookupFieldControl { FieldName = InternalName };
            }
        }
    }
}
